﻿using System;
using System.Collections.Generic;
using System.Windows.Forms;
using AutomatedDecisionMaking.Class;
using AutomatedDecisionMaking.Class.Algorithms;

namespace AutomatedDecisionMaking.GUI.BayesWindow
{
    public partial class FrmBayes : Form
    {
        #region Pola prywatne, konstruktor i podstawowa obsługa okna
        private readonly BayesAlgorithm _mAlgorithm; /// Algorytm realizujący obliczenia
        private readonly FrmAddDescription _mFrmAdd; /// Okno edycji decyzji dla algorytmu Bayes'a
        
        ///<summary>
        /// Konstrukotr okna analizy Bayes'owskiej
        /// </summary>
        /// <param name="decisionMatrix">Macierz decyzji użytkownika</param>
        public FrmBayes(DecisionMatrix decisionMatrix)
        {
            InitializeComponent();
            _mAlgorithm = new BayesAlgorithm(decisionMatrix);
            _mFrmAdd = new FrmAddDescription();
            ShowPanel1();
        }

        /// <summary>
        /// Metoda przygotowująca wygląd okna przed jego uruchomieniem.
        /// </summary>
        /// <param name="sender">Obiekt wysyłajacy zdarzenie</param>
        /// <param name="e">Obsługiwane zdarzenie</param>
        private void FrmBayes_Load(object sender, EventArgs e)
        {
            CreatWeightsTable();
            CreateConditionProbabilityTable();
            CreateConditionTable();
        }

        /// <summary>
        /// Przygotowanie wyglądu tabeli wag na podstawie ilości scenariuszy wprowadzonych przez użytkownika.
        /// </summary>
        private void CreatWeightsTable()
        {
            for (int i = 0; i < _mAlgorithm.GetDecisionMatrix().ScenarioList.Count; ++i)
            {
                this.p1DataGridView_Weights.Rows.Add();
                this.p1DataGridView_Weights.Rows[i].HeaderCell.Value = "S" + (i + 1);
            }
        }

        /// <summary>
        /// Przygotowanie wyglądu tabeli prawdopodobieństw warunkowych na podstawie 
        /// ilości scenariuszy wprowadzonych przez użytkownika.
        /// </summary>
        private void CreateConditionProbabilityTable()
        {
            for (int i = 0; i < _mAlgorithm.GetDecisionMatrix().ScenarioList.Count; ++i)
                this.p2DataGridView_ConditionProbabilityTable.Columns.Add("S" + (i + 1), "S" + (i + 1));

        }

        /// <summary>
        /// Przygotowanie wyglądu tabeli bayes'owskiej na podstawie ilości scenariuszy wprowadzonych przez użytkownika.
        /// </summary>
        private void CreateConditionTable()
        {
            for (int i = 0; i < _mAlgorithm.GetDecisionMatrix().ScenarioList.Count; ++i)
            {
                this.p3DataGridView_ConditionTable.Rows.Add();
                this.p3DataGridView_ConditionTable.Rows[i].HeaderCell.Value = "S" + (i + 1);
            }
        }
         
        /// <summary>
        /// Wyświetlenie Panelu nr 1
        /// </summary>
        private void ShowPanel1()
        {
            Panel1.Visible = true;
            Panel2.Visible = false;
            Panel3.Visible = false;
            Panel4.Visible = false;
        }

        /// <summary>
        /// Wyświetlenie Panelu nr 2
        /// </summary>
        private void ShowPanel2()
        {
            Panel1.Visible = false;
            Panel2.Visible = true;
            Panel3.Visible = false;
            Panel4.Visible = false;
        }

        /// <summary>
        /// Wyświetlenie Panelu nr 3
        /// </summary>
        private void ShowPanel3()
        {
            Panel1.Visible = false;
            Panel2.Visible = false;
            Panel3.Visible = true;
            Panel4.Visible = false;
        }

        /// <summary>
        /// Wyświetlenie Panelu nr 4
        /// </summary>
        private void ShowPanel4()
        {
            Panel1.Visible = false;
            Panel2.Visible = false;
            Panel3.Visible = false;
            Panel4.Visible = true;
        }
        #endregion

        #region Obsługa przycisków nawigacji
        /// <summary>
        /// Metoda obsługująca zdarzenie wygenerowane podczad kliknięcia
        /// przycisku next na panelu 1
        /// </summary>
        /// <param name="sender">Obiekt wywołujący zdarzenie</param>
        /// <param name="e">Rodzaj zdarzenie jakie wystąpiło</param>
        private void p1ButtonNext_Click(object sender, EventArgs e)
        {
            try
            {
                double sum = 0;
                for (int i = 0; i < _mAlgorithm.GetDecisionMatrix().ScenarioList.Count; ++i)
                    sum += Convert.ToDouble(p1DataGridView_Weights.Rows[i].Cells[0].Value);

                if (sum != 1)
                    throw new ArgumentOutOfRangeException("Suma prawdopodobieństw z poza zakresu!");

                _mAlgorithm.Reset();
                ShowPanel2();
            }
            catch (Exception exception)
            {
                MessageBox.Show("Nie wprowadzono prawdopodobieństw scenariuszy lub\nsuma prawdopodobieństw jest różna od 1!",
                                "INFORMACJA!",
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Information);

                for (int i = 0; i < _mAlgorithm.GetDecisionMatrix().ScenarioList.Count; ++i)
                    p1DataGridView_Weights.Rows[i].Cells[0].Value = "";
            }
        }

        /// <summary>
        /// Metoda obsługująca zdarzenie wygenerowane podczad kliknięcia
        /// przycisku prev na panelu 2
        /// </summary>
        /// <param name="sender">Obiekt wywołujący zdarzenie</param>
        /// <param name="e">Rodzaj zdarzenie jakie wystąpiło</param>
        private void p2ButtonPrev_Click(object sender, EventArgs e)
        {
            ShowPanel1();
        }

        /// <summary>
        /// Metoda obsługująca zdarzenie wygenerowane podczad kliknięcia
        /// przycisku next na panelu 2
        /// </summary>
        /// <param name="sender">Obiekt wywołujący zdarzenie</param>
        /// <param name="e">Rodzaj zdarzenie jakie wystąpiło</param>
        private void p2ButtonNext_Click(object sender, EventArgs e)
        {
            try
            {
                UpdateWindow();
                ShowPanel3();    
            }
            catch (ArgumentNullException exception)
            {
                _mAlgorithm.Reset();
                MessageBox.Show("Nie wypełniono wszystkich komórek tabeli prawdopodobieństw warunkowych!\nWypełnij komórki, aby kontynuować!",
                                "INFORMACJA!",
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Information);
            }
        }

        /// <summary>
        /// Metoda obsługująca zdarzenie wygenerowane podczad kliknięcia
        /// przycisku prev na panelu 3
        /// </summary>
        /// <param name="sender">Obiekt wywołujący zdarzenie</param>
        /// <param name="e">Rodzaj zdarzenie jakie wystąpiło</param>
        private void p3ButtonPrev_Click(object sender, EventArgs e)
        {
            ShowPanel2();
        }

        /// <summary>
        /// Metoda obsługująca zdarzenie wygenerowane podczad kliknięcia
        /// przycisku next na panelu 3
        /// </summary>
        /// <param name="sender">Obiekt wywołujący zdarzenie</param>
        /// <param name="e">Rodzaj zdarzenie jakie wystąpiło</param>
        private void p3ButtonNext_Click(object sender, EventArgs e)
        {
            ShowPanel4();
        }

        /// <summary>
        /// Metoda obsługująca zdarzenie wygenerowane podczad kliknięcia
        /// przycisku prev na panelu 4
        /// </summary>
        /// <param name="sender">Obiekt wywołujący zdarzenie</param>
        /// <param name="e">Rodzaj zdarzenie jakie wystąpiło</param>
        private void p4ButtonPrev_Click(object sender, EventArgs e)
        {
            ShowPanel3();
        }

        /// <summary>
        /// Metoda obsługująca zdarzenie wygenerowane podczad kliknięcia
        /// przycisku begin na panelu 4
        /// </summary>
        /// <param name="sender">Obiekt wywołujący zdarzenie</param>
        /// <param name="e">Rodzaj zdarzenie jakie wystąpiło</param>
        private void p4ButtonBegin_Click(object sender, EventArgs e)
        {
            ShowPanel1();
        }
        #endregion

        #region Obsługa Panelu 1
        /// <summary>
        /// Metoda obsługująca zdarzenie generowane przy zakończeniu edytowania komorki tabeli wag
        /// dla scenariuszy.
        /// </summary>
        /// <param name="sender">Obiekt wysyłajacy zdarzenie</param>
        /// <param name="e">Obsługiwane zdarzenie</param>
        private void p1DataGridView_Weights_CellEndEdit(object sender, DataGridViewCellEventArgs e)
        {
            try
            {
                double doubleValue;
                string cellValue = p1DataGridView_Weights.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString();
                if (!(double.TryParse(cellValue, out doubleValue)))
                {
                    MessageBox.Show("Wprowadzona wartość musi być liczbą z zakresu <0; 1>!",
                                    "INFORMACJA!",
                                    MessageBoxButtons.OK,
                                    MessageBoxIcon.Information);

                    p1DataGridView_Weights.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = "";
                }
                else if (doubleValue < 0 || doubleValue > 1)
                {
                    MessageBox.Show("Wprowadzona wartość musi być liczbą z zakresu <0; 1>!",
                                    "INFORMACJA!",
                                    MessageBoxButtons.OK,
                                    MessageBoxIcon.Information);

                    p1DataGridView_Weights.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = "";
                }
            }
            catch(NullReferenceException exception){}
        }
        #endregion

        #region Obsługa Panelu 2
        /// <summary>
        /// Metoda generująca zdarzenie po naciśnięciu przycisku dodaj warunek.
        /// </summary>
        /// <param name="sender">Obiekt wysyłajacy zdarzenie</param>
        /// <param name="e">Obsługiwane zdarzenie</param>
        private void p2Button_Add_Click(object sender, EventArgs e)
        {
            int rowsNo = this.p2DataGridView_ConditionProbabilityTable.Rows.Count;

            _mFrmAdd.Text = "Warunki - OPIS";
            _mFrmAdd.TextBox_Description.Text = "";
            if (_mFrmAdd.ShowDialog() == DialogResult.OK)
            {
                p2ListBox_Contition.Items.Add(_mFrmAdd.TextBox_Description.Text);
                this.p2DataGridView_ConditionProbabilityTable.Rows.Add();
                this.p2DataGridView_ConditionProbabilityTable.Rows[rowsNo].HeaderCell.Value = "W" + (rowsNo + 1);

                //Aktualizacja panelu 3
                p3CheckedListBox_Condition.Items.Add(_mFrmAdd.TextBox_Description.Text);

            }
        }

        /// <summary>
        /// Metoda generująca zdarzenie po naciśnięciu przycisku edytuj warunek.
        /// </summary>
        /// <param name="sender">Obiekt wysyłajacy zdarzenie</param>
        /// <param name="e">Obsługiwane zdarzenie</param>
        private void p2Button_Edit_Click(object sender, EventArgs e)
        {
            int index = p2ListBox_Contition.SelectedIndex;
            if(index>-1)
            {
                _mFrmAdd.Text = "Scenariusze - OPIS";
                _mFrmAdd.TextBox_Description.Text = p2ListBox_Contition.Items[index].ToString();
                if (_mFrmAdd.ShowDialog() == DialogResult.OK)
                {
                    p2ListBox_Contition.Items[index] = _mFrmAdd.TextBox_Description.Text;
                    p3CheckedListBox_Condition.Items[index] = _mFrmAdd.TextBox_Description.Text;
                }
            }
            else
                MessageBox.Show(
                    "Nie zaznaczono żadnego warunku do modyfikacji!\nZaznacz warunek aby kontynuować!",
                    "INFORMACJA!",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Information);
        }

        /// <summary>
        /// Metoda generująca zdarzenie po naciśnięciu przycisku usuń zaznaczony warunek.
        /// </summary>
        /// <param name="sender">Obiekt wysyłajacy zdarzenie</param>
        /// <param name="e">Obsługiwane zdarzenie</param>
        private void p2Button_Delete_Click(object sender, EventArgs e)
        {
            int index = p2ListBox_Contition.SelectedIndex;

            if (index > -1)
            {
                this.p2DataGridView_ConditionProbabilityTable.Rows.RemoveAt(index);
                p2ListBox_Contition.Items.RemoveAt(index);
                p3CheckedListBox_Condition.Items.RemoveAt(index);
            }
            else
                MessageBox.Show("Nie zaznaczono żadnego warunku do usunięcia!",
                                "UWAGA!",
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Information);
        }

        /// <summary>
        /// Metoda obsługująca zdarzenie generowane przy zakończeniu edytowania komorki tabeli prawdopodobieństwa
        /// wystąpienie poszczególnych warunków dla wprowadzonych scenariuszy.
        /// </summary>
        /// <param name="sender">Obiekt wysyłajacy zdarzenie</param>
        /// <param name="e">Obsługiwane zdarzenie</param>
        private void p2DataGridView_ConditionProbabilityTable_CellEndEdit(object sender, DataGridViewCellEventArgs e)
        {
            try
            {
                double doubleValue;
                string cellValue = p2DataGridView_ConditionProbabilityTable.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString();
                if (!(double.TryParse(cellValue, out doubleValue)))
                {
                    MessageBox.Show("Wprowadzona wartość musi być liczbą z zakresu (0; 1>!",
                                    "INFORMACJA!",
                                    MessageBoxButtons.OK,
                                    MessageBoxIcon.Information);

                    p2DataGridView_ConditionProbabilityTable.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = "";
                }
                else if (doubleValue <= 0 || doubleValue > 1)
                {
                    MessageBox.Show("Wprowadzona wartość musi być liczbą z zakresu (0; 1>!",
                                    "INFORMACJA!",
                                    MessageBoxButtons.OK,
                                    MessageBoxIcon.Information);

                    p2DataGridView_ConditionProbabilityTable.Rows[e.RowIndex].Cells[e.ColumnIndex].Value = "";
                }
            }catch(NullReferenceException exception){}
        }
        #endregion

        #region Obsługa Panelu 3
        /// <summary>
        /// Aktualizacja zawartości okna analizy Bayes'owskiej bo uruchomieniu algorytmu.
        /// </summary>
        public void UpdateWindow()
        {
            var weightsScenario = new List<double>();
            for (int i = 0; i < _mAlgorithm.GetDecisionMatrix().ScenarioList.Count; ++i)
                weightsScenario.Add(Convert.ToDouble(p1DataGridView_Weights.Rows[i].Cells[0].Value));

            var weightsConditions = new List<List<double>>();
            for (int i = 0; i < p2DataGridView_ConditionProbabilityTable.Rows.Count; ++i)
            {
                var row = new List<double>();
                for (int j = 0; j < p2DataGridView_ConditionProbabilityTable.Columns.Count; ++j)
                {
                    var cellValue = p2DataGridView_ConditionProbabilityTable.Rows[i].Cells[j].Value;
                    if(cellValue == null)
                        throw new ArgumentNullException("Nie wprowadzono danych we wszystkie komórki!");
                    row.Add(Convert.ToDouble(cellValue));
                }
                weightsConditions.Add(row);
            }

            _mAlgorithm.SetScenarioWeights(weightsScenario);
            _mAlgorithm.SetConditionWeights(weightsConditions);
            _mAlgorithm.CalculateSolution();
        }

        /// <summary>
        /// Metoda obsługująca zdarzenie generowane przy wybraniu odpowiedniego elementu z listy
        /// checkbox. Czyli po wybraniu odpowiedniego warunku dla którego mają być wyświetlane wyniki.
        /// </summary>
        /// <param name="sender">Obiekt wysyłajacy zdarzenie</param>
        /// <param name="e">Obsługiwane zdarzenie</param>
        private void p3CheckedListBox_Condition_SelectedIndexChanged(object sender, EventArgs e)
        {
            p4ListBox_WeightedValue.Items.Clear();

            var conditionTable = _mAlgorithm.GetConditionTableList();
            int index = p3CheckedListBox_Condition.SelectedIndex;
            
            for(int i=0; i<conditionTable[index].Count; ++i)
            {
                var row = conditionTable[index][i];
                for (int j = 0; j < row.Count; ++j)
                    p3DataGridView_ConditionTable.Rows[i].Cells[j].Value = row[j];
            }

            var weightedVal = _mAlgorithm.GetWeightedValuesStr();
            for (int i = 0; i < _mAlgorithm.GetDecisionMatrix().DecisionList.Count; ++i)
                p4ListBox_WeightedValue.Items.Add(weightedVal[index][i]);

            p4TextBox_MaxWeightedVal.Text = _mAlgorithm.GetMaxWeightedVals()[index].ToString();

            p4Label_Decision.Text = _mAlgorithm.GetDecisions()[index];

            p4Label_Condition.Text = p3CheckedListBox_Condition.Items[index].ToString();
        }

        /// <summary>
        /// Metoda obsługująca zdarzenie generowane gdy zmienu się zaznaczony element na liście
        /// checkbox. Zapewnia, że w danej chwili będzie zaznaczony tylko jeden element.
        /// </summary>
        /// <param name="sender">Obiekt wysyłajacy zdarzenie</param>
        /// <param name="e">Obsługiwane zdarzenie</param>
        private void p3CheckedListBox_Condition_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            if (p3CheckedListBox_Condition.CheckedItems.Count == 1)
            {
                Boolean isCheckedItemBeingUnchecked = (e.CurrentValue == CheckState.Checked);
                if (isCheckedItemBeingUnchecked)
                {
                    e.NewValue = CheckState.Checked;
                }
                else
                {
                    Int32 checkedItemIndex = p3CheckedListBox_Condition.CheckedIndices[0];
                    p3CheckedListBox_Condition.ItemCheck -= p3CheckedListBox_Condition_ItemCheck;
                    p3CheckedListBox_Condition.SetItemChecked(checkedItemIndex, false);
                    p3CheckedListBox_Condition.ItemCheck += p3CheckedListBox_Condition_ItemCheck;
                }
            }
        }
        #endregion

        
    }
}
